﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework.Graphics;
using System.IO;
using Daybreak.Core.Rendering.Pipeline;

namespace Daybreak.Core.Rendering.Materials
{
    public class FxShader : Shader
    {
        public string Name;

        public string ScriptClass;
        public string ScriptOrder;
        public string ScriptOutput;
        private bool standardsglobal;
        private bool _useTangents;

        private FxShader(Effect effect)
        {
            _effect = effect;

            Init();
        }

        public static FxShader Compile(string fxFile)
        {
            CompiledEffect effCode = Effect.CompileEffectFromFile(fxFile, null, null, CompilerOptions.None, Microsoft.Xna.Framework.TargetPlatform.Windows);

            if (!effCode.Success)
                return null;

            Effect effect = new Effect(Engine.GraphicsDevice, effCode.GetEffectCode(), CompilerOptions.None, null);

            bool valid = false;

            for (int i = 0; i < effect.Techniques.Count; i++)
            {
                effect.CurrentTechnique = effect.Techniques[i];

                if (effect.CurrentTechnique.Validate())
                {
                    valid = true;

                    break;
                }
            }

            if (!valid)
                return null;

            FxShader sh = new FxShader(effect);
            sh.Name = Path.GetFileName(fxFile);

            return sh;

        }

        private void Init()
        {
            int cnt = _effect.Parameters.Count;
            standardsglobal = true;

            for (int i = 0; i < cnt; i++)
            {
                if (!AddAttribute(_effect.Parameters[i]))
                    standardsglobal = false;
            }
        }

        private bool AddAttribute(EffectParameter param)
        {
            if (Parameter.HasSemantic(param, SasSemantic.standardsglobal))
            {
                ProcessStandardsGlobal(param);
                return true;
            }

            SasSemantic sem = SasStorage.GetKnownSemantic(param.Semantic);

            string uiType = null;
            string uiName = null;

            bool hasWidget = Parameter.HasUiWidget(param, out uiType, out uiName);

            if (hasWidget)
            {
                if (string.Compare(uiType, "color", true) == 0)
                {
                    TweakableParameter p = AddColor(param, sem);
                    p.UiWidget = uiType;
                    p.UiName = uiName;
                    return true;
                }

                if (string.Compare(uiType, "slider", true) == 0)
                {
                    TweakableParameter p = AddFloat(param, sem);
                    p.UiWidget = uiType;
                    p.UiName = uiName;

                    // TODO update min max slider values

                    return true;
                }

                TweakableParameter pp = AddMaterialAttribute(param, sem);

                if (pp == null)
                    return false;

                pp.UiWidget = uiType;
                pp.UiName = uiName;

                return true;
            }

            EffectAnnotation objAnn = param.Annotations["Object"];

            if (objAnn == null && sem != SasSemantic._unknown)
            {
                AddObjectAttribute(param, sem);
                return true;
            }

            if (objAnn != null && sem != SasSemantic._unknown)
            {
                string obj = objAnn.GetValueString();

                if (obj == "PointLight")
                {
                    AddFrameAttribute(param, sem, SceneObject.PointLight);
                    return true;
                }

            }
            if (param.ParameterType == EffectParameterType.Sampler ||
                param.ParameterType == EffectParameterType.Sampler1D ||
                param.ParameterType == EffectParameterType.Sampler2D ||
                param.ParameterType == EffectParameterType.Sampler3D)
                return true;

            if (param.ParameterType == EffectParameterType.Single &&
                param.ParameterClass == EffectParameterClass.Scalar)
            {
                AddMaterialAttribute(param, SasSemantic.none, new FloatValue());
                return true;
            }

            if (param.ParameterType == EffectParameterType.Single &&
                param.ParameterClass == EffectParameterClass.Vector)
            {
                if (param.ColumnCount == 1)
                    return AddMaterialAttribute(param, SasSemantic.none, new FloatValue()) != null;

                if (param.ColumnCount == 2)
                    return AddMaterialAttribute(param, SasSemantic.none, new Float2Value()) != null;

                if (param.ColumnCount == 3)
                    return AddMaterialAttribute(param, SasSemantic.none, new Float3Value()) != null;

                if (param.ColumnCount == 4)
                    return AddMaterialAttribute(param, SasSemantic.none, new Float4Value()) != null;

            }


            return false;
        }

        private void ProcessStandardsGlobal(EffectParameter param)
        {
            EffectAnnotation classAnon = param.Annotations["ScriptClass"];

            if (classAnon != null)
                ScriptClass = classAnon.GetValueString();

            EffectAnnotation orderAnon = param.Annotations["ScriptOrder"];

            if (orderAnon != null)
                ScriptOrder = orderAnon.GetValueString();

            EffectAnnotation outAnon = param.Annotations["ScriptOutput"];

            if (outAnon != null)
                ScriptOutput = outAnon.GetValueString();

            EffectAnnotation tanAnon = param.Annotations["UseTnagents"];

            _useTangents = false;

            if (tanAnon != null)
                _useTangents = tanAnon.GetValueBoolean();

        }

        public override string ToString()
        {
            if (standardsglobal)
                return "[Standard] FxShader:" + Name;

            return "[Unknown] FxShader:" + Name;

        }

        public override bool UseTangents
        {
            get { return _useTangents; }
        }

        public bool IsStandard
        {
            get { return standardsglobal; }
        }

    }
}
